/* Try to find the ip of the given mac.
 * 
 * It puts the interface in promiscue mode. It waits for a packet 
 * that it says which ip has the mac given. It stores the result in ip_found.
 * 
 * Functions:
 * int ip_finder(char *mac_to_find, char *interface, struct in_addr *ip_found )
 *
 * 08/11/06 Guess the ip based in arp packets 
 * 14/11/06 Added the support to udp and ip packet
 *
 * 
 * Ignacio Lopez
 * nacholopez at gmail dot com
 * 
*/

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>

/* Looking for private ips */
#define PRIVATE_IN_CLASSC(a)            ((((in_addr_t)(a)) & 0xFFFF0000) == 0xc0a80000)
#define PRIVATE_IN_CLASSB(a)            ((((in_addr_t)(a)) & 0xFFF00000) == 0xac100000)
#define PRIVATE_IN_CLASSA(a)            ((((in_addr_t)(a)) & 0xFF000000) == 0x0a000000)

/* Global variables 
 */

/* Control when the ip has been found */
int found=0;

/* Mac address to find */
char *mac_address=NULL;

/* Ip founded */
struct in_addr *ip_founded=NULL;

/*
 * Return !=0 if the ip is private.
 */
inline int is_private(struct in_addr *address)
{
	u_int32_t ip;
	ip = ntohl(address->s_addr);

	if (PRIVATE_IN_CLASSC(ip)){
		printf("Ip privada. (C)\n");
		return 1;
	}
	if (PRIVATE_IN_CLASSA(ip)){
		printf("Ip privada. (A)\n");
		return 1;
	}
	if (PRIVATE_IN_CLASSB(ip)){
		printf("Ip privada. (B)\n");
		return 1;
	}
	return 0;
}


/*
 * dissec an ip packet
 */
inline void handle_ip_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
	const struct ether_header *eth_header; 
	const struct iphdr *ip_header;
	struct in_addr source_ip, dest_ip;

	eth_header = (struct ether_header*) packet;
	ip_header = (struct ip_header*)(packet + sizeof(struct ether_header));
	
	memcpy(&source_ip,&ip_header->saddr,sizeof(struct in_addr));
	memcpy(&dest_ip,&ip_header->daddr,sizeof(struct in_addr));

        fprintf(stdout, "IP source[%s]-", inet_ntoa(source_ip));
	fprintf(stdout, "IP destination[%s]\n", inet_ntoa(dest_ip));
	
	/* COMPARATION CODE */
	u_char*  ether_lookingfor=ether_aton(mac_address);

	/* If the sender has the mac we are looking for and is a private ip */	
        if ( memcmp(ether_lookingfor,eth_header->ether_shost,ETH_ALEN)==0 && is_private(&ip_header->saddr) ){
		memcpy(ip_founded,&source_ip,sizeof(struct in_addr));
		found = 1;
		printf("La ip es %s:\n", inet_ntoa(source_ip));
		return;
	}
	
        if (memcmp(ether_lookingfor,eth_header->ether_dhost,ETH_ALEN)==0 && is_private(&ip_header->daddr) ){
		memcpy(ip_founded,&dest_ip,sizeof(struct in_addr));
		found = 1;
		printf("La ip es %s:\n", inet_ntoa(dest_ip));
		return;
	}
}

/*
 * dissec an arp packet
 */
void handle_arp_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
	const struct ether_header *eth_header; 
	const struct arphdr *arp_header;
	const struct ether_arp *arp_packet;
	struct in_addr ip_src, ip_dst;
	u_int16_t operation;

	eth_header = (struct ether_header*) packet;
	
        /* COMPARATION CODE */
	u_char*  ether_lookingfor=ether_aton(mac_address);
	

	/* MUST TO BE IMPLEMENTED */
	/* Check the ethernet payload == 28 */
	arp_packet = (struct ether_arp*) (packet + sizeof(struct ether_header));
	arp_header = (struct arphdr*)arp_packet;

	/* I'm only interested in arp request and arp replies */
	operation = ntohs(arp_header->ar_op);
	if (operation != ARPOP_REQUEST && operation != ARPOP_REPLY){
		printf("Unknown Arp operation\n");
		return;
	}
	
	bcopy((void *)arp_packet->arp_spa, (void *) &ip_src, sizeof(struct in_addr));
	bcopy((void *)arp_packet->arp_tpa, (void *) &ip_dst, sizeof(struct in_addr));

	if (operation == ARPOP_REQUEST){
		printf("Arp request\n");
	}
	else if (operation == ARPOP_REPLY){
		printf("Arp reply\n");
	}
	
	if (memcmp(ether_lookingfor,arp_packet->arp_sha,ETH_ALEN)== 0) {
		memcpy(ip_founded,&ip_src,sizeof(struct in_addr));
		found = 1;
		printf("La ip es %s:\n",inet_ntoa(ip_src));
	}
	
	fprintf(stdout,"Sender: mac[%s] ",ether_ntoa(arp_packet->arp_sha));
	fprintf(stdout,"ip[%s]\n",inet_ntoa(ip_src));
	fprintf(stdout,"Destination: mac[%s] ",ether_ntoa(arp_packet->arp_tpa));
	fprintf(stdout,"ip[%s]\n",inet_ntoa(ip_dst));
}

/*
 * dissect/print packet
 */
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{

	static int count = 1;                   /* packet counter */
	
	/* declare pointers to packet headers */
	const struct ether_header *eth_header;  /* net/ethernet.h */
	u_int16_t eth_type;

	printf("\nPacket number %d:\n", count);
	count++;
	
	/* define ethernet header */
	eth_header = (struct ether_header*)(packet);
	eth_type = ntohs(eth_header->ether_type);

	fprintf(stdout,"ethernet header source: %s ",ether_ntoa(eth_header->ether_shost));
        fprintf(stdout,"destination: %s \n",ether_ntoa(eth_header->ether_dhost));
	
	if (ntohs(eth_header->ether_type) == ETHERTYPE_ARP){
		printf("Packet type: ARP\n");
		handle_arp_packet(args,header,packet);
		return;
	}
	if (ntohs(eth_header->ether_type) == ETHERTYPE_IP){
		printf("Packet type: IP\n");
		handle_ip_packet(args,header,packet);
		return;
	}

	printf("Packet unknown\n");
	return;

}

void print_error_msg(char *msg)
{
	fprintf(stderr,msg);
	fprintf(stderr,"\n");
}


/* Try to find the IP from the mac. It stores the result in ip_found. */
int ip_finder(char *mac, char *dev, struct in_addr *ip_found )
{
	char errbuf[PCAP_ERRBUF_SIZE];		/* error buffer */
	pcap_t *handle;				/* packet capture handle */
	char filter_exp[] = "arp or ip"; 	/* filter expression */
	struct bpf_program fp;			/* compiled filter program (expression) */

	mac_address=mac;
	ip_founded=ip_found;

	/* print capture info */
	printf("Device: %s\n", dev);

	/* open capture device */
	handle = pcap_open_live(dev, BUFSIZ, 1, -1, errbuf);
	if (handle == NULL) {
		print_error_msg(errbuf);
		return -1;
	}

	/* make sure we're capturing on an Ethernet device */
	if (pcap_datalink(handle) != DLT_EN10MB){
		print_error_msg("The network interface is not ethernet");
		return -1;
	}

	/* compile the filter expression */
	if (pcap_compile(handle, &fp, filter_exp, 0, 0) == -1){
		print_error_msg(pcap_geterr(handle));
		return -1;
	}

	/* apply the compiled filter */
	if (pcap_setfilter(handle, &fp) == -1){
		print_error_msg(pcap_geterr(handle));
		return -1;
	}

	do {
		/* now we can set our callback function */
		pcap_loop(handle, 1, got_packet,  NULL );
	}
	while(found==0);


	/* cleanup */
	pcap_freecode(&fp);
	pcap_close(handle);
	return 0;
}

